package net.molchat.android.cont;


import net.molchat.android.core.Setting;
import net.molchat.android.mess.Message;


/**
 * Shared class for ContRoom, ContOne and maybe other graphical classes.
 * 
 * @author Valentine
 * 
 */
public abstract class ContGraph extends Cont {


//Сообщения
protected Message mess[] = new Message[Setting.MAX_ONSCREEN]; // Очередь сообщений
protected int messSel; // Указатель на выделенное сообщение


//Разметка
protected int messDrawF; // Указатель на первое отрисовываемое сообщение
protected int messDraw; // Указатель на текущее отрисовываемое сообщение

protected int messTot; // Общее количество сообщений в очереди
protected int messTotH; // Messages total height

protected int messF; // Указатель на первое сообщение
protected int messL = -1; // Указатель на последнее добавленное сообщение в очереди (messL = (messF + messTot - 1) % net.molchat.android.mess.lenght)

protected int dispF; // Смещение первой строки
protected int dispTot; // Displacement to the message block top from screen top

protected int textH; // Высота окна текстовых сообщений
protected int textW; // Ширина окна текстовых сообщений

protected int stickDownH = 15; // Height for stick down messages, points


public int birthDay = -1, birthMon = -1, birthYear = -1; // День, месяц, год создания (рождения, для перс. контактов)


//======================================================================== nextP
/**
 * Calculate and return next array pointer.
 * 
 */
protected int nextP(int pointer) {

	if (++pointer >= Setting.MAX_ONSCREEN) {
		return 0;
	} else {
		return pointer;
	}
}


//======================================================================== prevP
/**
 * Calculate and return previous array pointer.
 * 
 */
protected int prevP(int pointer) {

	if (--pointer < 0) {
		return Setting.MAX_ONSCREEN - 1;
	} else {
		return pointer;
	}
}


//===================================================================== delFirst
/**
 * Delete first message.
 * 
 */
private void delFirst() {

	if (messTot <= 0) {
		return;
	}

	// Change full net.molchat.android.mess height
	messTotH -= mess[messF].hTot;

	// Move selector
	if (messSel == messF) {
		messSel = nextP(messSel);
	}

	// Move "draw first pointer"
	if (messDrawF == messF) { // First message is on screen
		// Set right dispF
		dispTot = 0;
		dispF = 0;
		messDrawF = nextP(messDrawF);
	} else { // First message isn't on screen
		dispTot -= mess[messF].hTot;
	}

	// Move First pointer
	messF = nextP(messF);

	messTot--;
}


//======================================================= Задать цвет сообщения
public void setMessColor(int messNum, int color) {

	mess[messNum].color = color;
}


//====================================================================== setSize
/**
 * Recalculating messages size for a new display size if needed.
 */
public synchronized void setSize(int width, int height) {

	if (textW != width || textH != height) {
		textW = width;
		textH = height;

		stickDownH = textH >> 5; // >>5 = 1/32

		if (messTot > 0) {

			messTotH = 0;
			dispF = 0;
			dispTot = 0;

			int nowP = messF - 1;
			int resDisplace = 0;
			do {
				nowP = nextP(nowP);
				mess[nowP].parse(textW);
				messTotH += mess[nowP].hTot;

				if (nowP == messDrawF) {
					resDisplace = messTotH;
				}
			} while (nowP != messL);

			dispF = 0; // Смещение первой строки
			messDrawF = messF; // Указатель на первое отрисовываемое сообщение
			move(resDisplace);
		}

	}
}


//============================================================ select by messNum
private boolean select(int messSel) {

	if (this.messSel == messSel) {
		return true;
	} else {
		this.messSel = messSel;
		return false;
	}
}


//=============================================================== select by X, Y
/**
 * 
 */
public synchronized boolean select(int x, int y) {

	if (messTot > 0) {

		boolean wasSelected = false;

		int messNow = messDrawF - 1;
		int hNow = -dispF;
		do {
			messNow = nextP(messNow);
			hNow += mess[messNow].hTot;
			if (hNow >= y) {
				wasSelected = select(messNow);
				break;
			}
		} while (hNow < textH && messNow != messL);

		return wasSelected;
	} else {
		return false;
	}
}


//======================================================================== selUp
public void selUp() {

	int scrHalf = textH >> 1;

	if (messTot > 0) {
		if (isFirstLater(messDrawF, messSel)) { // Selector is too high
			int nowP = messDrawF;
			int nowH = dispF;
			while (nowH < messTotH) {
				nowP = prevP(nowP);
				nowH += mess[nowP].hTot;
				if (nowP == messSel) {
					break;
				}
			}
			move(-nowH - stickDownH);
		} else if (messSel == messF) { // Selector is on the first message
			move(-scrHalf);
		} else {

			int messToShow = prevP(messSel);
			int nowP = messDrawF;
			int nowH = -dispF + mess[nowP].hTot;
			int lastScrP = nowP;

			while (nowH < textH) {
				if (nowP == messSel) {
					break;
				}
				nowP = nextP(nowP);
				nowH += mess[nowP].hTot;
				lastScrP = nowP;
			}

			if (nowP != messSel) { // Selector is too low
				messSel = lastScrP;
			} else {
				// Check if we can move newly selected message to center of the screen
				if (isFirstLater(messToShow, messDrawF)) { // Yes, we can
					messSel = messToShow;
					// Calculate centerY of the newly selected message
					int hTot = mess[messDrawF].hTot;
					int messNow = messDrawF;
					while ((messNow = nextP(messNow)) != messToShow) {
						hTot += mess[messNow].hTot;
					}
					hTot += mess[messToShow].hTot >> 1;
					int toMove = hTot - (scrHalf + dispF);
					if (toMove < 0) {
						move(toMove);
					}
				} else if (messToShow == messDrawF) { // Maybe. Need to calculate distance
					int toMove = scrHalf - ((mess[messDrawF].hTot >> 1) - dispF);
					move(-Math.min(toMove, scrHalf));
					messSel = prevP(messSel);
				} else { // Exactly no
					move(-scrHalf);
				}
			}
		}

	}
}


//====================================================================== selDown
public void selDown() {


	int scrHalf = textH >> 1;

	if (messTot > 0) {

		if (isFirstLater(messDrawF, messSel)) {

			// Selector is too high
			messSel = messDrawF; // Move selector to the first message on screen

		} else if (messSel == messL) { // Last message selected
			move(scrHalf);
		} else {

			// Check if selector is too low
			int messToShow = nextP(messSel);
			int nowP = messDrawF;
			int nowH = -dispF + mess[nowP].hTot;
			//			int lastScrP = nowP;

			while (nowH < textH) {
				if (nowP == messToShow) {
					break;
				}
				nowP = nextP(nowP);
				nowH += mess[nowP].hTot;
				//				lastScrP = nowP;
			}

			if (nowP != messToShow) { // Selector is too low
				move(scrHalf);
			} else {
				messSel = messToShow;
				int toCenter = nowH - mess[messToShow].hTot - scrHalf;
				if (toCenter < 0) {
					toCenter = 0;
				}
				move(Math.min(toCenter, scrHalf));
			}

		}
	}

}


//================================================= Преобразование в байт-массив
public abstract byte[] toByte();


//========================================================================= move
public synchronized void move(int offsetY) {

	if (offsetY > 0) {
		int maxOffset = messTotH - dispTot - textH;
		if (maxOffset >= offsetY) {
			displace(offsetY);
		} else if (maxOffset > 0) {
			displace(maxOffset);
		}
	} else {
		if (dispTot >= -offsetY) {
			displace(offsetY);
		} else if (dispTot > 0) {
			displace(-dispTot);
		}
	}
}


//======================================================= Текущее селектировано?
public boolean isSel() {

	if (messDraw == messSel) {
		return true;
	} else {
		return false;
	}
}


//======================================================================== compP
/**
 * Calculate and return previous array pointer.
 * 
 */
protected boolean isFirstLater(int firstP, int secondP) {

	if (firstP < messF) {
		firstP += mess.length;
	}

	if (secondP < messF) {
		secondP += mess.length;
	}

	if (firstP > secondP) {
		return true;
	} else {
		return false;
	}
}


//================================================ Получить выделенное сообщение
public Message getSel() {

	if (messTot > 0) {
		return mess[messSel];
	} else {
		return null;
	}
}


//============================================================ Получить смещение
public int getDisplace() {

	return dispF;
}


//=================================================== Заполнение из байт-массива
public abstract void fromByte(byte[] content);


//===================================================== Очистка списка сообщений
public void clear() {

	messTot = 0;
	dispF = 0;
	dispTot = 0;
}


//===================================================================== displace
/**
 * @param points
 *          > 0 means messages goes UP on the screen.
 * 
 */
protected void displace(int points) {

	dispTot += points;
	dispF += points;

	if (points > 0) {
		while (dispF > mess[messDrawF].hTot && (messDrawF != messL)) {
			dispF -= mess[messDrawF].hTot;
			messDrawF = nextP(messDrawF);
		}
	} else {
		while (dispF < 0 && (messDrawF != messF)) {
			messDrawF = prevP(messDrawF);
			dispF += mess[messDrawF].hTot;
		}
	}
}


//===================================================================== addToEnd
/**
 * Add new message to the end of the message array.
 */
private void addToEnd(Message newMess) {

	messL = nextP(messL);
	mess[messL] = newMess;
	messTotH += newMess.hTot;

	if (messTotH > textH
			&& (messTotH - dispTot - textH - newMess.hTot < stickDownH)) { // Stick down
		displace(messTotH - textH - dispTot);
	}

	messTot++;
}


//====================================================================== addMess
public synchronized int addMess(Message newMess) {

	// Parse new message if needed
	if ((newMess.res == null) && (textW > 0)) {
		newMess.parse(textW);
	}


	// Delete first (old) message
	if (messTot >= Setting.MAX_ONSCREEN) {
		delFirst();
	}

	// Add new
	addToEnd(newMess);

	return messL;
}


//==================================== Получить первое отрисовываемое сообщение
public Message getFirst() {

	messDraw = messDrawF;
	return mess[messDraw];
}


//================================== Получить следующее отрисовываемое сообщение
public Message getNext() {

	if (messDraw != messL) {
		messDraw = nextP(messDraw);
		return mess[messDraw];
	} else {
		return null;
	}
}


//=================================================================== getTitText
public abstract String getTitText();


}
